﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows;
using System.Windows.Media;

namespace UmlControlLibrary
{
    public class UmlContextMenu : ContextMenu
    {
        private Dictionary<string, MenuItem> placeHolders = new Dictionary<string, MenuItem>();
        public ContextMenuEventArgs MenuEventArgs = null;

        public object OwnerElementDataContext
        {
            get;
            set;
        }

        public UmlContextMenu()
            : base()
        {
            Background = Brushes.WhiteSmoke;
        }

        public virtual void SetMenuItemContext(Control item, object context)
        {
            if (item != null)
            {
                // If Tag is a context menu or is empty, we can update it.
                // otherwise, we do NOT touch it. As assignment is recursive, 
                // the context will be available from parent node if really needed.
                if ((item.Tag == null) || (item.Tag is ContextMenu))
                    item.Tag = this;

                item.DataContext = context;
                if (item is MenuItem)
                {
                    foreach (object child in (item as MenuItem).Items)
                        if (child is Control)
                            SetMenuItemContext(child as Control, context);
                }
            }
        }

        private void CleanupPlaceHolders()
        {
            foreach (string item in placeHolders.Keys)
            {
                MenuItem placeHolder = placeHolders[item];
                ItemCollection siblings = (placeHolder.Parent as ItemsControl).Items as ItemCollection;
                int currentIndex = siblings.IndexOf(placeHolder);
                for (int i = siblings.Count - 1; i > currentIndex; i--)
                {
                    object child = siblings.GetItemAt(i);
                    if ((child is MenuItem) && ((child as MenuItem).Name == placeHolder.Name))
                        siblings.RemoveAt(i);
                }
            }
        }

        private void AppendMenuItem(SpecificMenuItem item, ItemCollection items, int index)
        {
            MenuItem menuItem = new MenuItem();
            menuItem.Header = item.Caption;
            menuItem.Name = item.Group;
            menuItem.Click += item.OnClick;
            items.Insert(index, menuItem);
        }

        private void AppendPlaceHolders(SpecificMenu specificMenu)
        {
            SpecificMenu commonItems = SpecificMenu.GetTypeMenu(OwnerElementDataContext.GetType());

            foreach (string item in placeHolders.Keys)
            {
                MenuItem placeHolder = placeHolders[item];
                ItemCollection siblings = (placeHolder.Parent as ItemsControl).Items as ItemCollection;
                int currentIndex = siblings.IndexOf(placeHolder) + 1;

                List<SpecificMenuItem> items = commonItems.GetItemGroup(item);
                if (items != null)
                    foreach (SpecificMenuItem menuItem in items)                       
                        AppendMenuItem(menuItem, siblings, currentIndex++);
                if ((specificMenu != null) && (specificMenu != commonItems))
                {
                    items = specificMenu.GetItemGroup(item);
                    if (items != null)
                        foreach (SpecificMenuItem menuItem in items)
                            AppendMenuItem(menuItem, siblings, currentIndex++);
                }
            }
        }

        public virtual void SetMenuContexts(ContextMenuEventArgs e, object ownerDataContext, object menuDataContext)
        {
            MenuEventArgs = e;
            SpecificMenu specificMenu = null;
            //if (ownerDataContext != menuDataContext)
            {
                Type t = (menuDataContext != null) ? menuDataContext.GetType() : null;
                specificMenu = SpecificMenu.GetTypeMenu(t);
            }
            OwnerElementDataContext = ownerDataContext;
            this.DataContext = menuDataContext;
            CleanupPlaceHolders();
            AppendPlaceHolders(specificMenu);
            foreach (Control item in Items)
            {
                item.Tag = this;
                SetMenuItemContext(item, this.DataContext);
            }
        }

        void UmlPropertyContextMenu_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            /*        foreach (Control item in Items)
                        if (!(item is Separator))
                            item.DataContext = this.DataContext;
              */
        }

        protected MenuItem GetSubMenu(string caption)
        {
            return GetMenuItem(caption, null);
        }

        protected MenuItem GetMenuPlaceHolder(string name)
        {
            MenuItem item = GetMenuItem("<" + name + ">", null);
            item.Name = name;
            item.Visibility = Visibility.Collapsed;
            placeHolders.Add(item.Name, item);
            return item;
        }

        protected Separator GetMenuSeparator()
        {
            return new Separator();
        }

        protected MenuItem GetMenuItem(string header, RoutedEventHandler onClick)
        {
            MenuItem item = new MenuItem();
            item.Header = header;
            if (onClick != null)
                item.Click += onClick;
            return item;
        }

        protected MenuItem GetMenuItem(BindingBase headerBinding, RoutedEventHandler onClick)
        {
            MenuItem item = new MenuItem();
            item.SetBinding(MenuItem.HeaderProperty, headerBinding);
            if (onClick != null)
                item.Click += onClick;
            return item;
        }
    }
}
